package com.dmz.oauth2.utils;

import java.nio.charset.StandardCharsets;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import cn.hutool.core.util.StrUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.impl.TextCodec;
import lombok.extern.log4j.Log4j2;

@Log4j2
public class JwtUtils {

    public final static String REQUEST_TOKEN = "token";

    /**
     * 一次性密钥，用于封装一次性token
     */
    private static final String DISPOSABLE_SECRET = "echronos_";

    /**
     * 一次性token的过期时间
     */
    private static final int DISPOSABLE_EXPIRE = 23 * 60 * 60;

    /**
     * 消息头 key
     */
    public static String REQUEST_HEADER = "Authorization";

    /**
     * 消息头传入类型 key
     */
    public static String CLIENT_TYPE = "Basic";
    /**
     * jwt中封装的jti标识
     */
    public static final String TOKEN_JWT_JTI = "jti";
    /**
     * jwt中封装的用户key
     */
    public static final String TOKEN_JWT_OPENID = "openId";
    /**
     * jwt中刷新token标识
     */
    public static final String TOKEN_JWT_AIT = "ait";
    /**
     * jwt中客户端标识
     */
    public static final String TOKEN_JWT_CLIENT_ID = "client_id";
    /**
     * 随机密钥位数
     */
    public static final Integer TOKEN_JWT_SECRET_COUNT = 16;
    /**
     * 客户端id
     */
    public static String TOKEN_CLIENT_ID = "client";

  

    /**
     * 生成一次性token
     *
     * @param infoMap
     * @return
     */
    public static String createDisposableToken(Map<String, Object> infoMap) {
        Calendar nowTime = Calendar.getInstance();
        nowTime.add(Calendar.SECOND, DISPOSABLE_EXPIRE);
        Date expireDate = nowTime.getTime();

        Map<String, Object> map = new HashMap<>(2);
        map.put("alg", "HS512");
        map.put("typ", "JWT");

        return Jwts.builder().setHeader(map)
                .setClaims(infoMap)
                .setSubject("chen")
                // 生成时间
                .setIssuedAt(new Date())
                // 过期时间
                .setExpiration(expireDate)
                .signWith(SignatureAlgorithm.HS512, getSignerKey())
                .compact();
    }

    /**
     * 校验token，主要校验一次性token，不校验登录token，若校验登录token使用{@link JwtUtils#validateToken(String, String)}
     *
     * @param token 需要验证的token
     * @return
     */
    public static Claims validateDisposableToken(String token) {
        return Jwts.parser()
                // 设置签名的秘钥
                .setSigningKey(getSignerKey())
                // 设置需要解析的jwt
                .parseClaimsJws(token).getBody();
    }

    /**
     * 校验token，并返回token封装的参数
     *
     * @param token 需要验证的token
     * @param key   校验密钥
     * @return
     */
    public static Claims validateToken(String token, String key) {
        return Jwts.parser()
                // 设置签名的秘钥
                .setSigningKey(getSignerKey(key))
                // 设置需要解析的jwt
                .parseClaimsJws(token).getBody();
    }

    /**
     * 生成刷新token
     *
     * @param map    封装信息
     * @param date   过期时间
     * @param secret 密钥
     * @return token
     */
    public static String createRefreshToken(Map<String, Object> map, Date date, String secret) {
        map.getOrDefault(TOKEN_JWT_AIT, UUID.randomUUID().toString());
        return createToken(map, date, secret);
    }

    /**
     * 根据封装信息、过期时间、密钥生成token
     *
     * @param map    封装信息
     * @param date   过期时间
     * @param secret 密钥
     * @return token
     */
    public static String createToken(Map<String, Object> map, Date date, String secret) {
        Map<String, Object> headerMap = new HashMap<>(2);
        headerMap.put("alg", "HS256");
        headerMap.put("typ", "JWT");

        map.putIfAbsent("client_id", "web-client");
        return Jwts.builder().setHeader(headerMap)
                .setClaims(map)
                .setSubject("echronos")
                // 生成时间
                .setIssuedAt(new Date())
                // 过期时间
                .setExpiration(date)
                .signWith(SignatureAlgorithm.HS256, getSignerKey(secret))
                .compact();
    }

    /**
     * 获取明文数据payload
     *
     * @param token
     * @return
     */
    public static String getTokenPayload(String token) {
        // 获取消息头中的token
        String[] split = token.split("\\.");
        if (split.length == 3) {
            String payload = split[1];
            if (StrUtil.isBlank(payload)) {
                return "{}";
            }
            try {
                String userJson = new String(TextCodec.BASE64URL.decode(payload), StandardCharsets.UTF_8);
                if (StrUtil.isBlank(userJson)) {
                    throw new RuntimeException("出错");
                }
                // 返回明文json数据
                return userJson;
            } catch (Exception e) {
                return null;
            }
        }
        return null;
    }

    /**
     * 将签名密钥进行编码
     *
     * @return
     */
    public static SecretKey getSignerKey() {
        return new SecretKeySpec(DISPOSABLE_SECRET.getBytes(), "AES");
    }

    /**
     * 将传入的签名密钥进行编码
     *
     * @param key
     * @return
     */
    public static SecretKey getSignerKey(String key) {
        return new SecretKeySpec(key.getBytes(), "AES");
    }
}
